<?php

class EGP_Image_Thumb
{

    /**
     * 用于存储图片数据信息
     *
     * @var array
     */
    protected $_info;

    /**
     * 构造方法
     *
     * @param string $sourceImage
     */
    public function __construct($sourceImage = null)
    {
        if (!is_null($sourceImage)) {
            $this->setSource($sourceImage);
        }
    }

    /**
     * 设定图片源文件
     *
     * @param  string  $sourceImage
     * @return EGP_Image_Thumb
     * @throws EGP_Exception
     */
    public function setSource($sourceImage)
    {
        if (!is_readable($sourceImage)) {
            throw new EGP_Exception('图片源文件不存在或无法加载');
        }

        $this->_info['orig_image'] = $sourceImage;
        $this->_info['orig_size']  = @getimagesize($sourceImage);
        switch ($this->_info['orig_size'][2]) {
            case 1  : $this->_info['im'] = imagecreatefromgif ($sourceImage); break; /*gif*/
            case 2  : $this->_info['im'] = imagecreatefromjpeg($sourceImage); break; /*jpg*/
            case 3  : $this->_info['im'] = imagecreatefrompng ($sourceImage); break; /*png*/
            case 6  : $this->_info['im'] = imagecreatefrombmp ($sourceImage); break; /*bmp*/
            case 15 : $this->_info['im'] = imagecreatefromwbmp($sourceImage); break; /*wbmp*/
            default :  throw new EGP_Exception('不支持的文件类型');
        }

        return $this;
    }

    /**
     * 生成图片缩略图
     *
     * @param  integer  $maxWidth    最大宽度
     * @param  integer  $maxHeight   最大高度
     * @param  boolean  $constraint  是否约束图片长宽比
     * @return EGP_Image_Thumb
     */
    public function make($maxWidth, $maxHeight = false, $constraint = true)
    {
        $origSize = &$this->_info['orig_size'];
        $im = &$this->_info['im'];
        $resizeByH = $resizeByW = false;
        $origSize[0] > $maxWidth && $maxWidth  && $resizeByW = true;
        $origSize[1] > $maxWidth && $maxHeight && $resizeByH = true;
        if ($resizeByH && $resizeByW) {
            $resizeByH = ($origSize[0] / $maxWidth < $origSize[1] / $maxHeight);
            $resizeByW = !$resizeByH;
        }
        if ($resizeByW) {
            if ($constraint) {
                $newW = $maxWidth;
                $newH = ($origSize[1] * $maxWidth) / $origSize[0];
            } else {
                $newW = $maxWidth;
                $newH = $origSize[1];
            }
        } elseif ($resizeByH) {
            if ($constraint) {
                $newW = ($origSize[0] * $maxHeight) / $origSize[1];
                $newH = $maxHeight;
            } else {
                $newW = $origSize[0];
                $newH = $maxHeight;
            }
        } else {
            $newW = $origSize[0];
            $newH = $origSize[1];
        }
        if ($newW != $origSize[0] || $newH != $origSize[1]) {
            $imN = imagecreatetruecolor($newW, $newH);
            imagecopyresampled($imN, $im, 0, 0, 0, 0, $newW, $newH, $origSize[0], $origSize[1]);
            imagedestroy($im);
            $this->_info['im'] = $imN;
        }
        $this->_info['orig_size'][0] = $newW;
        $this->_info['orig_size'][1] = $newH;
        return $this;
    }

    /**
     * 按指定区域大小截取图片
     *
     * @param  integer  $startX  开始 X 轴坐标
     * @param  integer  $startY  开始 Y 轴坐标
     * @param  integer  $endX    结束 X 轴坐标
     * @param  integer  $endY    结束 Y 轴坐标
     * @return EGP_Image_Thumb
     */
    public function crop($startX, $startY, $endX = false, $endY = false){
        $im = &$this->_info['im'];
        $origSize = &$this->_info['orig_size'];
        $endX == false && $endX = $origSize[0] - $startX;
        $endY == false && $endY = $origSize[1] - $startY;
        $width  = $endX - $startX;
        $height = $endY - $startY;
        $imN = imagecreatetruecolor($width, $height);
        imagecopy($imN, $im, 0, 0, $startX, $startY, $width, $height);
        imagedestroy($im);
        $this->_info['im'] = $imN;
        $this->_info['orig_size'][0] = $width;
        $this->_info['orig_size'][1] = $height;
        return $this;
    }

    /**
     * 按指定尺寸截取图片，以中心为起点
     *
     * @param  integer  $width   图片宽度
     * @param  integer  $height  图片高度
     * @param  integer  $moveX   X 轴坐标偏移像素
     * @param  integer  $moveY   Y 轴坐标偏移像素
     * @return EGP_Image_Thumb
     */
    public function cropCenter($width, $height, $moveX = 0, $moveY = 0){
        $origSize = &$this->_info['orig_size'];
        $centerX  = $origSize[0] / 2;
        $centerY  = $origSize[1] / 2;
        $topX = $centerX - $width / 2;
        $topY = $centerY - $height / 2;
        $endX = $centerX + $width / 2;
        $endY = $centerY + $height / 2;
        $this->crop($topX + $moveX, $topY + $moveY, $endX + $moveX, $endY + $moveY);
        return $this;
    }

    /**
     * 创建图片文件，如果存在则自动覆盖
     *
     * @param  string   $outputImage  输出的图片文件名及路径
     * @param  string   $outputAs     输出格式 (gig, wbmp, jpeg)
     * @param  integer  $quality      图片质量 (0-100)，jpeg 格式下有效
     * @return boolean
     * @throws EGP_Exception
     */
    public function build($outputImage = null, $outputAs = null, $quality = 100) {
        is_null($outputImage) && $outputImage = $this->_info['orig_image'];
        !is_null($outputAs)   && $outputAs = strtolower(substr(strrchr($outputImage, '.'), 1));

        if (!EGP_Io::mkdir(dirname($outputImage))) {
            throw new EGP_Exception('无法创建缩略图存储目录：' . dirname($outputImage));
        }

        switch ($outputAs) {
            case 'bmp'  :
            case 'wbmp' : $result = @imagewbmp($this->_info['im'], $outputImage); break;
            case 'gif'  : $result = @imagegif ($this->_info['im'], $outputImage); break;
            case 'png'  : $result = @imagepng ($this->_info['im'], $outputImage); break;
            default     : $result = @imagejpeg($this->_info['im'], $outputImage, $quality);
        }
        return $result ? $result : false;
    }

    /**
     * 输出缩略图
     *
     * @param  boolean  $build        是否创建缩略图文件
     * @param  string   $outputImage  输出的图片文件名及路径
     * @param  string   $outputAs     输出格式 (gig, wbmp, jpeg)
     * @param  integer  $quality      图片质量 (0-100)，jpeg 格式下有效
     * @throws EGP_Exception
     */
    public function output($build = false, $outputImage = null, $outputAs = null, $quality = 100)
    {
        $mime = $this->_info['orig_size']['mime'];
        $build && $this->build($outputImage, $outputAs, $quality);
        header("Content-type: $mime");
        switch ($mime) {
            case 'bmp'  :
            case 'wbmp' : $image = imagewbmp($this->_info['im']); break;
            case 'gif'  : $image = imagegif ($this->_info['im']); break;
            case 'png'  : $image = imagepng ($this->_info['im']); break;
            default     : $image = imagejpeg($this->_info['im']);
        }
    }

}

if (!function_exists('imagecreatefrombmp')) {
    /**
     * 从 BMP 文件或 URL 新建一图像
     *
     * @param  string  $filename
     * @return resource
     */
    function imagecreatefrombmp($filename) {
        if (!($f1 = fopen($filename, "rb")))
            return false;
        $file = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14));
        if ($file['file_type'] != 19778)
            return false;
        $bmp = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
                      '/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
                      '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));
        $bmp['colors'] = pow(2, $bmp['bits_per_pixel']);
        if ($bmp['size_bitmap'] == 0)
            $bmp['size_bitmap'] = $file['file_size'] - $file['bitmap_offset'];
        $bmp['bytes_per_pixel'] = $bmp['bits_per_pixel'] / 8;
        $bmp['bytes_per_pixel2'] = ceil($bmp['bytes_per_pixel']);
        $bmp['decal'] = ($bmp['width']*$bmp['bytes_per_pixel'] / 4);
        $bmp['decal'] -= floor($bmp['width'] * $bmp['bytes_per_pixel'] / 4);
        $bmp['decal'] = 4 - ( 4 * $bmp['decal']);
        if ($bmp['decal'] == 4)
            $bmp['decal'] = 0;
        $palette = array();
        if ($bmp['colors'] < 16777216)
            $palette = unpack('V' . $bmp['colors'], fread($f1, $bmp['colors'] * 4));
        $IMG = fread($f1, $bmp['size_bitmap']);
        $vide = chr(0);
        $res = imagecreatetruecolor($bmp['width'], $bmp['height']);
        $P = 0;
        $y = $bmp['height'] - 1;
        while ($y >= 0) {
            $x = 0;
            while ($x < $bmp['width']) {
                if ($bmp['bits_per_pixel'] == 24) {
                    $color = unpack('V', substr($img, $p, 3) . $vide);
                } elseif ($bmp['bits_per_pixel'] == 16) {
                    $color = unpack('n', substr($img, $p, 2));
                    $color[1] = $palette[$color[1] + 1];
                } elseif ($bmp['bits_per_pixel'] == 8) {
                    $color = unpack('n', $vide . substr($img, $P, 1));
                    $color[1] = $palette[$color[1] + 1];
                } elseif ($bmp['bits_per_pixel'] == 4){
                    $color = unpack("n", $vide . substr($img, floor($P), 1));
                    if (($P * 2) % 2 == 0)
                        $color[1] = ($color[1] >> 4);
                    else
                        $color[1] = ($color[1] & 0x0F);
                    $color[1] = $palette[$color[1] + 1];
                } elseif ($bmp['bits_per_pixel'] == 1) {
                    $color = unpack("n", $vide . substr($img, floor($P), 1));
                        if (($P * 8) % 8 == 0) $color[1] =  $color[1]         >> 7;
                    elseif (($P * 8) % 8 == 1) $color[1] = ($color[1] & 0x40) >> 6;
                    elseif (($P * 8) % 8 == 2) $color[1] = ($color[1] & 0x20) >> 5;
                    elseif (($P * 8) % 8 == 3) $color[1] = ($color[1] & 0x10) >> 4;
                    elseif (($P * 8) % 8 == 4) $color[1] = ($color[1] & 0x8 ) >> 3;
                    elseif (($P * 8) % 8 == 5) $color[1] = ($color[1] & 0x4 ) >> 2;
                    elseif (($P * 8) % 8 == 6) $color[1] = ($color[1] & 0x2 ) >> 1;
                    elseif (($P * 8) % 8 == 7) $color[1] = ($color[1] & 0x1 );
                    $color[1] = $palette[$color[1] + 1];
                } else {
                    return false;
                }
                imagesetpixel($res, $x, $y, $color[1]);
                $X++;
                $p += $bmp['bytes_per_pixel'];
            }
            $y--;
            $p += $bmp['decal'];
        }
        fclose($f1);
        return $res;
    }
}
